#pragma once
#include "IndexedGeometry.h"
#include "Material.h"
#include "AABBMesh.h"
#include "IndexBufferMaterial.h"
#include <string>
#include <vector>
#include <fstream>
using namespace std;

class Material;
class AABBMesh;
class Skeleton;
class VertexList;
class TriangleList;
class Cone;

typedef vector<IndexBufferMaterial*>::const_iterator IBMIT;

//! Basic 3D Object that contains the vertex data and indexed geometry.
/**
* @class Mesh
* Objects of this class just contain a vertex buffer and one or more
* IndexBuffers to be a able to draw a 3D object on the screen.
* This class also sets the material to use for the indexbuffer.
* Each indexbuffer can have its own material.
* @author Koen Samyn
*/
class ObjMesh
{
public:
	//! Constructs an empty Mesh object.
	ObjMesh();
	//! Constructs a Mesh object with the given VertexBuffer and IndexedGeometry
	/**
	@param vl a pointer to the VertexList source data.
	@param ig a pointer to a valid IndexedGeometry object.
	@see VertexBuffer
	@see IndexedGeometry
	*/
	ObjMesh(VertexList * vl,IndexedGeometry * ig);

	//! Constructs a Mesh object with the given VertexBuffer,IndexedGeometry and Material object
	/**
	@param vl a pointer to the VertexList source data.
	@param ig a pointer to a valid IndexedGeometry object.
	@param pMaterial a pointer to the Material object.
	@see VertexBuffer
	@see IndexedGeometry
	*/
	ObjMesh(VertexList * vl,IndexedGeometry * ig, Material* pMaterial);

	//! Deletes this mesh object.
	virtual ~ObjMesh(void);
	/**
		Sets the name of this object.
		@return the name of the object.
	*/
	void SetName(tstring name);
	/**
	* Adds an indexbuffer to the list of indexbuffers.
	* @param ibm the indexbuffer to add.
	*/
	void AddIndexBuffer(IndexBufferMaterial* ibm){
		m_IndexBuffers.push_back(ibm);
	}

	/**
	* Retesselates the mesh to optimize for a spotlight.
	* To avoid problems with index in the case of multiple triangle
	* list, new vertices will be inserted at the end of the vertexlist.
	* @param cone the cone that defines the spotlight.
	*/
	void Retesselate(Cone* cone);
	/**
	* Retesselate one triangle.
	* @param cone the definition of the light cone.
	* @param triList the triangle list object.
	* @param posOffset the offset of the position semantic in the vertexlist.
	* @param currentTriangleIndex the index of the current triangle.
	* @param index1 the first index of the triangle.
	* @param index2 the second index of the triangle.
	* @param index3 the third index of the triangle.
	*/ 
	void RetesselateTriangle(Cone* cone,TriangleList* triList,int posOffset,int currentTriangleIndex,int index1, int index2, int index3 );

	/**
	* Sets the vertexbuffer for this mesh object.
	* @param vertexbuffer the vertexbuffer to set.
	*/
	void SetVertexList(VertexList* vertexList){
		m_pVertexList = vertexList;
	}

	/**
	* Gets the vertexlist object associated with this mesh object.
	* @return the vertexlist object of this object.
	*/
	VertexList* GetVertexList() const{
		return m_pVertexList;
	}

	/**
	* Adds a semantic to the vertexlist with a default value.
	* @param sd the semantic descriptr
	* @param defaultValue the default value.
	*/
	void AddSemanticToVertexList(SemanticDescriptor sd, FXValue* value);

	/**
	* Gets the name of the 3D object.
	* @return the name of the object
	*/
	tstring GetName();
	/**
	* Checks if tangents need to be generated for this mesh
	* @return true if we need tangents, false otherwise.
	*/
	bool NeedsTangents();
	/**
	* Sets the AABBMesh for this Mesh object.
	* @param aabbMesh the aabbMesh for this Mesh object.
	*/
	void SetAABBMesh(AABBMesh* aabbMesh){m_AABBMesh=aabbMesh;}
	/**
	* Gets the AABBMesh for this Mesh object.
	* @return the axis aligned bounding box for this character.
	*/
	AABBMesh* GetAABBMesh(){return m_AABBMesh;}
	/**
	* Sets the skeleton for this mesh.
	* @param skeleton the skeleton for the mesh.
	*/
	void SetSkeleton(Skeleton* skeleton);
	/**
	* Returns the skeleton for this mesh.
	* @return the skeleton for this mesh.
	*/
	Skeleton* GetSkeleton(){
		return m_pSkeleton;
	}
	/**
	* Test the AABBMESH with a ray test.
	* @param ray the ray to test the aabbmesh with.
	* @param distance an out parameter with the distance.
	* @param t0 the minimum distance.
	* @param t1 the maximum distance.
	* @return true if the AABBMesh was hit, false otherwise.
	*/
	bool HitTest(Ray& ray,float& distance,float t0, float t1);
	/**
	* Returns the beginning of the indexbuffer vector
	* @return the begin iterator for the vertexbuffer.
	*/
	IBMIT GetBegin(){return m_IndexBuffers.begin();}
	/**
	* Returns the end of the indexbuffer vector
	* @return the end iterator for the vertexbuffer.
	*/
	IBMIT GetEnd(){return m_IndexBuffers.end();}
	/**
	* Adds an id to the vector of light ids.
	* @param id the light id to add.
	*/
	void AddLightID(int id){
		m_LightIDs.push_back(id);
	}

	/**
	* Returns the number of lights ids.
	* @return the number of light ids.
	*/
	int GetNrOfLightIDs(){
		return m_LightIDs.size();
	}

	/**
	* Returns the light id with the given index.
	* @param index the index in the vector of lights ids.
	* @return the light id at the given index.
	*/
	int GetLightID(int index){
		return m_LightIDs[index];
	}
private:
	/**
	* The name for this mesh.
	*/
	tstring m_Name;
	/**
	* The vertexbuffer with the vertices.
	*/
	VertexList * m_pVertexList;
	/**
	* A list of indexbuffers (usually 1) used
	* by the indexgeometry call.
	*/
	vector<IndexBufferMaterial*> m_IndexBuffers;
	/**
	* The skeleton with bone information for this mesh object.
	*/
	Skeleton * m_pSkeleton;
	/**
	* The AABB bounding box for this mesh
	*/
	AABBMesh * m_AABBMesh;
	/**
	* A vector of light ids.
	*/
	vector<int> m_LightIDs;
	// -------------------------
	// Disabling default copy constructor and default assignment operator.
	// If you get a linker error from one of these functions, your class is internally trying to use them. This is
	// an error in your class, these declarations are deliberately made without implementation because they should never be used.
	// -------------------------
	ObjMesh(const ObjMesh& t);
	ObjMesh& operator=(const ObjMesh& t);
};
